package com.learning.netease.io.nio;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

/**
 * ClassName: NioServer
 * Description: NIO 服务端。直接基于非阻塞的写法
 * Date: 2021/1/10 22:33 【需求编号】
 *
 * @author Sam Sho
 * @version V1.0.0
 */
@Slf4j
public class NioServer {

    public static void main(String[] args) throws Exception {
        // 创建网络服务端
        try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
            // 设置为非阻塞模式
            serverSocketChannel.configureBlocking(false);
            // 绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(8080));
            log.info("~~~启动成功~~~");

            do {
                // 获取新tcp连接通道，不会阻塞，会直接返回，一定要判断是否非空。
                SocketChannel socketChannel = serverSocketChannel.accept();
                // tcp请求 读取/响应。
                if (socketChannel != null) {
                    log.info("收到新连接 : {}", socketChannel.getRemoteAddress());

                    // 默认是阻塞的,一定要设置为非阻塞
                    socketChannel.configureBlocking(false);
                    try {
                        // 申请一块 ByteBuffer，用于读取数据
                        ByteBuffer requestBuffer = ByteBuffer.allocate(1024);
                        // 非阻塞，需要循环的 read 数据
                        // 问题，无法处理多个请求连接。原因在于，不断判断数据的读取
                        while (socketChannel.isOpen() && socketChannel.read(requestBuffer) != -1) {
                            // 长连接情况下，需要手动判断数据有没有读取结束 (此处做一个简单的判断: 超过0字节就认为请求结束了)
                            if (requestBuffer.position() > 0) {
                                break;
                            }
                        }
                        if (requestBuffer.position() == 0) {
                            // 如果没数据了, 则不继续后面的处理
                            continue;
                        }
                        requestBuffer.flip();
                        byte[] content = new byte[requestBuffer.limit()];
                        requestBuffer.get(content);
                        log.info("收到数据，来自 {}。数据为 {}", socketChannel.getRemoteAddress(), new String(content));

                        // 响应结果 200
                        String response = "HTTP/1.1 200 OK\r\n" +
                                "Content-Length: 11\r\n\r\n" +
                                "Hello World";
                        ByteBuffer buffer = ByteBuffer.wrap(response.getBytes());
                        while (buffer.hasRemaining()) {
                            // 非阻塞
                            socketChannel.write(buffer);
                        }
                    } catch (IOException e) {
                        log.error(e.getMessage(), e);
                    }
                }
            } while (true);
        }
        // 问题，无法处理多个请求连接。原因在于，不断判断数据的读取
        // 用到了非阻塞的API, 在设计上,和BIO可以有很大的不同.继续改进
    }
}
